"Les cours de neeko.fr"

Retour en haut

Android : bienvenue sur le chemin

Android : bienvenue sur le chemin

Quelques pistes pour la suite

Aller plus loin avec les vues

La solution la plus simple pour construire une vue est de placer tous les éléments dans le fichier layout de l'activity.

Il existe d'autres solutions pour construire des applications plus complexe.

A partir du layout XML de l'activity :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/myLayout" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="LAYOUT PRINCIPAL" /> </LinearLayout>

Construire des vues "à la main"

En réalité, tous les éléments présent dans le layout XML (LinearLayout, TextView, Button, ...) sont en fait des objets qui sont instanciés automatiquement par le système Android.

Il est possible de les construire "à la main" (directement en Java) :

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //construction d'un boutton Button anotherButton = new Button(this); anotherButton.setText(R.string.app_name); //recuperation du layout pour y placer le nouveau boutton LinearLayout theLayout = (LinearLayout) findViewById(R.id.myLayout); theLayout.addView(anotherButton); }

Le service LayoutInflater

Il est parfois interessant de "dupliquer" des éléments complexes de vues. Le system-service LayoutInflater est prévu pour ça.

On peut le récupérer comme les autres services, ou grace à une méthode de "convenance" :

LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);

ou

LayoutInflater inflater = this.getLayoutInflater();

Il faut commencer par créer un layout "secondaire" contenant les vues à répéter:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="LAYOUT SECONDAIRE:" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/theLabel" /> </LinearLayout>

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Service de "gonflage" de layout LayoutInflater inflater = this.getLayoutInflater(); //gonfle le XML ... View sub1 = inflater.inflate(R.layout.sub_layout, null); //recupere le label dans ce XML TextView textView1 = (TextView) sub1.findViewById(R.id.theLabel); textView1.setText("Premier"); //recuperation du layout pour y placer les nouvelles vues LinearLayout theLayout = (LinearLayout) findViewById(R.id.myLayout); theLayout.addView(sub1); }

On aurait pu utiliser plusieurs fois le même layout secondaire :

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Service de "gonflage" de layout LayoutInflater inflater = this.getLayoutInflater(); //gonfle le 1er XML ... View sub1 = inflater.inflate(R.layout.sub_layout, null); //recupere le label dans ce XML TextView textView1 = (TextView) sub1.findViewById(R.id.theLabel); textView1.setText("Premier"); //gonfle le 2e XML ... View sub2 = inflater.inflate(R.layout.sub_layout, null); //recupere le label dans ce XML TextView textView2 = (TextView) sub2.findViewById(R.id.theLabel); textView2.setText("Second"); //recuperation du layout pour y placer les nouvelles vues LinearLayout theLayout = (LinearLayout) findViewById(R.id.myLayout); theLayout.addView(sub1); theLayout.addView(sub2); }

A noter que la méthode "findViewById" peut s'appeller sur l'activity, mais aussi sur n'importe quelle vue (View) ! Il est important de l'utiliser pour chercher une vue lorqu'on est certain de n'avoir qu'une seule fois l'id présent.

TextView textViewX = (TextView) findViewById(R.id.theLabel); textViewX.setText("Qui-est-ce ?");

Mise en parallèle

En principe, tous le code s'execute séquentiellement, ligne par ligne. Une méthode ne s'execute pas avant qu'une autre ait terminée.

Cela n'est pas toujours pratique. Parfois, une action longue doit etre executée en arriere-plan.

On utilise pour cela les Threads.

Comportement standard

Voici un exemple standard, sans utiliser les Threads :

Soit 2 classes simples :

public class MockAction1 { public void faireAction() { Log.d("NORMAL", "DEBUT ACTION 1"); for(int i=0; i < 50 ; i++){ Log.d("NORMAL", "MOCK ACTION 1"); } Log.d("NORMAL", "FIN ACTION 1"); } }

public class MockAction2 { public void faireAction() { Log.d("NORMAL", "DEBUT ACTION 2"); for(int i=0; i < 50; i++){ Log.d("NORMAL", "MOCK ACTION 2"); } Log.d("NORMAL", "FIN ACTION 2"); } }

Dans le onCreate de l'activity :

MockAction1 action1 = new MockAction1(); MockAction2 action2 = new MockAction2(); Log.d("NORMAL", "Avant"); action1.faireAction(); Log.d("NORMAL", "Milieu"); action2.faireAction(); Log.d("NORMAL", "Apres");

Le résultat de cette execution, comme prévu :

05-28 15:17:49.239: NORMAL : Avant 05-28 15:17:49.249: NORMAL : MOCK ACTION 1 05-28 15:17:49.249: NORMAL : MOCK ACTION 1 05-28 15:17:49.249: NORMAL : MOCK ACTION 1 05-28 15:17:49.249: NORMAL : MOCK ACTION 1 ...tous les autres MOCK ACTION 1 05-28 15:17:49.299: NORMAL : Milieu 05-28 15:17:49.299: NORMAL : MOCK ACTION 2 05-28 15:17:49.299: NORMAL : MOCK ACTION 2 05-28 15:17:49.299: NORMAL : MOCK ACTION 2 05-28 15:17:49.309: NORMAL : MOCK ACTION 2 ...tous les autres MOCK ACTION 2 05-28 15:17:49.339: NORMAL : Apres

Avec les Threads

On modifie un peu les 2 classes :

public class MockAction1 implements Runnable { @Override public void run() { Log.d("THREAD", "DEBUT ACTION 1"); for(int i=0; i < 50 ; i++){ Log.d("THREAD", "MOCK ACTION 1"); } Log.d("THREAD", "FIN ACTION 1"); } }

public class MockAction2 implements Runnable { @Override public void run() { Log.d("THREAD", "DEBUT ACTION 2"); for(int i=0; i < 50; i++){ Log.d("THREAD", "MOCK ACTION 2"); } Log.d("THREAD", "FIN ACTION 2"); } }

et le onCreate de l'activity :

MockAction1 action1 = new MockAction1(); MockAction2 action2 = new MockAction2(); Thread thread1 = new Thread(action1); Thread thread2 = new Thread(action2); Log.d("THREAD", "Avant"); thread1.start(); Log.d("THREAD", "Milieu"); thread2.start(); Log.d("THREAD", "Apres");

Et le résultat, plus surprenant :

05-28 15:22:58.919: THREAD : Avant 05-28 15:22:58.919: THREAD : Milieu 05-28 15:22:58.929: THREAD : DEBUT ACTION 1 05-28 15:22:58.929: THREAD : MOCK ACTION 1 ...quelques autres MOCK ACTION 1 05-28 15:22:58.959: THREAD : Apres 05-28 15:22:58.979: THREAD : DEBUT ACTION 2 05-28 15:22:58.979: THREAD : MOCK ACTION 2 ...quelques autres MOCK ACTION 2 05-28 15:22:58.989: THREAD : MOCK ACTION 1 ...quelques autres MOCK ACTION 1 05-28 15:22:58.989: THREAD : FIN ACTION 1 05-28 15:22:59.019: THREAD : MOCK ACTION 2 ...quelques autres MOCK ACTION 2 05-28 15:22:59.019: THREAD : FIN ACTION 2

L'interet c'est que le code s'execute (presque) en parallèle. Sur des systèmes avec un seul processeur (ou un seul coeur), le code "saute" d'un thread à l'autre très régulièrement. Dans un systeme multi-coeur, l'execution peut être vraiment en même temps.

Dans tous les cas, le code executé ne "bloque" pas le thread principal. L'interet pour nous est de pouvoir executer un code long et continuer d'avoir une application active.

Lorque l'on appelle "start" sur un objet Thread, le systeme va créer un thread "en arriere plan" et executer le code de l'objet Runnable dans ce thread.

Attention, car toute modification de l'UI doit forcement se faire dans le thread principal, appellé le "UI thread" sur Android.

Il est possible, depuis un thread d'arriere plan, de demander l'execution d'un code sur le thread principal avec la méthode runOnUiThread(Runnable r) sur l'activity. On se sert de ce mécanisme pour mettre à jour l'affichage apres un long calcul, ou une requête Web par exemple.

Classes anonymes

Lorsque l'on a besoin d'implementer une interface, on crée une classe qui implemente effectivement les méthodes de cette interface.

Il faut savoir qu'il existe un moyen rapide d'implémenter une interface. Cette technique est tout à fait valable, mais ne doit pas être utilisée systématiquement car elle n'encourage pas un découpage clair entre les objets.

Cela dit, elle est parfois utilisée ponctuellement. Elle consiste à implémenter "à la volée" une instance d'une interface ou d'une classe abstraite. On doit implémenter concretement les méthodes de l'interface, comme s'il s'agissait d'une classe normale.

OnClickListener listener = new OnClickListener() { @Override public void onClick(View v) { //code execute si click ! Log.d("CLICK", "Click !!"); } }; Button myButton = findViewById(R.id.theBtn); myButton.setOnClickListener(listener);

ou directement

Button myButton = findViewById(R.id.theBtn); myButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //code execute si click ! Log.d("CLICK", "Click !!"); } });

Il est possible d'utiliser ce principe pour n'importe quelle interface ou classe abstraite.

LocationManager locationManager; locationManager = (LocationManager) this.getSystemService(LOCATION_SERVICE); locationManager.requestLocationUpdates(GPS_PROVIDER, 0, 0, new LocationListener() { @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.d("GPS", "Status change"); } @Override public void onProviderEnabled(String provider) { Log.d("GPS", "Fournisseur ON"); } @Override public void onProviderDisabled(String provider) { Log.d("GPS", "Fournisseur OFF"); } @Override public void onLocationChanged(Location location) { Log.d("GPS", "Nouvelle position : " + location.getLatitude()); } });

this.combat = new Combat(); this.combat.character1 = player; this.combat.character2 = boss; this.combat.listener = new CombatListener() { @Override public void combatTurnStart(Combat c) { Log.d("COMBAT", "Combat commence"); } @Override public void combatEndExAequo(Combat c) { Log.d("COMBAT", "Combat termine"); } @Override public void combatEnd(Combat c, Character winner) { Log.d("COMBAT", "Combat gagnant : " + winner.getName()); } };

L'inconvénient majeur est que l'on ne peux pas créer plusieurs instances identique sans dupliquer le code.

Au dela d'Android 2.3

Depuis la sortie d'Android 2.3, de nouvelles fonctionnalités ont fait leur apparition sur Android.

Pour profiter de la totalité de ces nouveautés, le plus simple est de choisir un SDK plus récent, avec comme inconviénient majeur de réduire considérablement le public potentiel de l'application.

Android support

Il est tout de même possible de profiter d'un certain nombre de nouvelles fonctionnalité grace à la library "Android Support V4".

L'idée de cette librairie est de permettre aux développeurs à partir de l'API 4 de profiter de nouveautés des plateformes supérieures.

Installation

Les Fragments

Les Fragments sont la fonctionnalité la plus interressante introduite dans la version 3.0 d'Android (API 11).

L'idée est qu'une Activity sera composée de plusieurs fragments plus ou moins indépendants pour représenter les différents éléments de la page.

Chaque fragment représentera un élément particulier : le menu, le "footer", la zone de contenu, la pub, .... Chaque fragment pourra gérer de son côté ses actions et ses interactivités. L'activity manipulera les fragments, mais chaque fragment pourra se gérer lui même.

Pour info, les fragments ont un cycle de vie qui ressemble à celui d'une activity :

Exemple d'intégration :
Main activity :

public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }

Layout XML du fragment (res/layout/footer_fragment.xml) :

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/legal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/copyright" /> <Button android:id="@+id/footerButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/more_info" /> </LinearLayout>

Classe FooterFragment :

public class FooterFragment extends Fragment implements OnClickListener { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.footer_fragment, container, false); Button btn = (Button) v.findViewById(R.id.footerButton); btn.setOnClickListener(this); return v; } @Override public void onClick(View v) { TextView text = (TextView) getView().findViewById(R.id.legal); text.setText(R.string.copyright_more); } }

Layout XML de l'activity

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <fragment android:name="com.example.fragmenttest.FooterFragment" android:id="@+id/viewer" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>

iOS et Objective-C

Les frameworks Android et iOS ont de nombreuses différences, mais reposent sur des principes assez proche.

Java est le langage pour Android, Objective-C est celui d'iOS (ou macOS).

Syntaxe des langages

La syntaxe est différente mais le principe est le même.

Les notions sont identiques : classes, méthodes, variables ...

En Java :

Personnage gandalf = new Personnage(); gandalf.setName("Gandalf"); gandalf.setLife(100); int damages = gandalf.attack(); Log.d("Combat", "Gandalf fait " + damages + " dégats.");

En Objective-C :

Personnage * gandalf = [[Personnage alloc] init]; [gandalf name: @"Gandalf"]; [gandalf life: 100]; int damages = [gandalf attack]; NSLog(@"Gandalf fait %d dégats.", damages);

Exemple d'application

ViewController.h

#import <UIKit/UIKit.h> @interface ViewController : UIViewController { IBOutlet UILabel * infoLabel; IBOutlet UIButton * button; IBOutlet UITextField * inputText; } - (IBAction) verifieMajorite:(id)sender; @end

ViewController.m

#import "ViewController.h" @implementation ViewController - (void) viewDidLoad { [super viewDidLoad]; [infoLabel setText:@"Veuillez entrer votre age"]; } - (IBAction) verifieMajorite:(id)sender{ NSLog(@"bouton click"); NSString * input = [inputText text]; int age = [input integerValue]; if (age >= 18){ [infoLabel setText:@"Vous êtes majeur !"]; } else { [infoLabel setText:@"Vous êtes mineur !"]; } } - (void) didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end